#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h> //struct addr_in
#include <arpa/inet.h>
#include "StdUdp.h"
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
struct StdUdp
{
    int sock;
};
Udp *UdpInit(const char *ip, unsigned short port)
{   
    Udp*u = (Udp*)malloc(sizeof(Udp));
    if(u == NULL)
    {
        printf("udp malloc");
        return NULL;
    }

     u->sock = socket(AF_INET,SOCK_DGRAM,0); //创建了一个套接字，并将其文件描述符存储在变量 u->sock 中
    if(u->sock == -1) //AF_INET：指定了套接字的地址族，这里是IPv4地址族，表示套接字将用于IPv4网络通信
    {
        perror("socket"); //SOCK_DGRAM：指定了套接字的类型，这里是数据报套接字类型，表示使用UDP协议进行通信
        free(u); //0：指定了协议类型，通常为0，表示让系统根据地址族和套接字类型自动选择合适的协议
        return NULL;
    }

    struct sockaddr_in addr; //struct sockaddr_in：这是一个结构体，用于存储IPv4套接字地址信息
    addr.sin_family = AF_INET; //将 addr 结构体中的 sin_family 成员设置为 AF_INET，表示使用IPv4地址族
    addr.sin_port = htons(port); //将端口号 port 转换为网络字节顺序（big-endian）后，赋值给 addr 结构体中的 sin_port 成员
    addr.sin_addr.s_addr = inet_addr(ip); //inet_addr() 函数用于将点分十进制的IP地址转换为网络字节顺序的32位二进制整数表示，存储在 sin_addr.s_addr 中

    if(bind(u->sock,(struct sockaddr*)&addr,sizeof(addr))<0) //将套接字 u->sock 绑定到指定的地址 addr。如果绑定失败，bind 函数会返回一个负值
    {
        perror("bind");
        free(u);
        return NULL;
    }
    return u;
}

bool UdpSend(Udp *u, void *p, size_t size, const char *destIP, unsigned short destPort)
{   
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(destPort);
    addr.sin_addr.s_addr = inet_addr(destIP);
    if(sendto(u->sock,p,size,0,(struct sockaddr*)&addr,sizeof(addr)) < 0) //使用 sendto 函数向指定的地址 addr 发送数据
    {
        perror("sendto");
        return false;
    }
    return true;
}

bool UdpRecv(Udp *u, void *p, size_t size)
{   
    struct sockaddr_in addr;

    memset(&addr,0,sizeof(addr));

    socklen_t len = sizeof(addr); //获取地址结构体的大小，以便在后续操作中使用。

    if(recvfrom(u->sock,p,size,0,(struct sockaddr*)&addr,&len) < 0) //使用 recvfrom 函数从套接字 u->sock 接收数据，并将接收到的数据存储到缓冲区 p 中。如果接收失败，recvfrom 函数会返回一个负值
    {
        perror("recvfrom");
        return false;
    }
    return true;
}

void ClearUdp(Udp *p)
{
    //关闭文件描述符
    close(p->sock);
    free(p);
}
